【Kotlin/Android Studio】Flowの使い方!collectやemit関数とは?

この記事からわかること

  • Android Studio/KotlinFlow使い方
  • 非同期処理を実装する方法
  • コルーチンストリームとは?
  • Kotlin Coroutinesとの違い
  • Flow builders種類作成方法
  • flowOf関数flow { ... }関数の使い方
  • collect関数emit関数の使い方

index

[open]

\ アプリをリリースしました /

みんなの誕生日

友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-

posted withアプリーチ

参考文献:公式リファレンス:Flow

環境

Flowとは?

interface Flow<out T>

AndroidのFlowとはコルーチン(非同期処理)の一種でコールドフロー特性を持ったデータストリームオブジェクトです。そもそもストリームとは日本語で「流れ」を意味する英単語であり、プログラミングの分野ではデータの入出力の流れを保持する抽象的なオブジェクトのことを指します。ストリームの中では変化するデータと「完了」または「例外」が発行されます。

RxSwiftとは?導入方法と使い方まとめ!ストリームを理解する

そしてそのストリームには値が常に流れるHot明示的に購読されている時のみ値が流れるColdがあり(もっと細かい特徴の違いはあるが。)、Flowは後者になります。データストリームなので複数の値を順番(シーケンシャル)に出力することができ、更新時にUI変更の起きやすいDB操作などに利用されることが多いです

また流れてくるデータに対してフィルタリングをかけたり、データを加工したりといったことも可能なため、リアルタイムでのデータの変化に対して柔軟な処理を実装することが可能となっています。

Kotlin Coroutinesとの使い分け

Androidでは他にも非同期処理を実装する方法としてKotlin Coroutinesなどがあります。Kotlin Coroutinesは非同期的な処理を同期的なコードのように記述できる特徴を持っています。

Flowはデータのストリーミング(データの連続生成)性を持った非同期処理を得意とするのに対し、Kotlin Coroutinesは単発的な非同期処理を得意とします。

Kotlin Coroutines

Flow


fun main() {
    runBlocking {
        // Deferredオブジェクトを取得
        val result1 = async { fetchDataFromRemote() }
        // 中身を取得
        val remoteData = result1.await()

        print("$remoteData")
    }
}

suspend fun fetchDataFromRemote(): String {
    delay(2000)
    return "リモートサーバーからフェッチ"
}

Flowオブジェクトの作成方法

Flowオブジェクトを作成するにはFlow builders(フロービルダー)を使用します。Flow buildersは以下のように複数用意されています。

flowOf関数

公式リファレンス:flowOf関数

fun <T> flowOf(vararg elements: T): Flow<T>

flowOf関数を使用してFlowオブジェクトを生成し、データの変換の流れを取得してみたいと思います。Web実行環境で動作確認できるようにimport文も記載しています。

import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.runBlocking

fun main() = runBlocking {
    val numFlow: Flow<Int> = flowOf(1, 2, 3, 4, 5)

    numFlow.collect { value ->
        print(value)
    }
}

1
2
3
4
5

flowOf関数は引数にデータとして流したい値を羅列して渡すだけでそのデータを流すFlowオブジェクトを作成する関数です。collect関数については後述しますが、これでデータを受け取ってる感じです。またrunBlockingに関してはKotlin Coroutinesのコードになります。

asFlow関数

asFlow関数は既存のコレクションやシーケンスなどをFlowに変換する関数です。ListやArray、Set、mapなどが変換可能です。

import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.runBlocking

fun main() = runBlocking {
    val numList = listOf(1, 2, 3, 4, 5)
    val numFlow: <Int> = numbersList.asFlow()

    numFlow.collect { value ->
        print(value)
    }
}

flow { ... }関数

fun <T> flow(block: suspend FlowCollector<T>.() -> Unit): Flow<T>

flow { ... }関数ブロック内でemitされたデータを出力するFlowオブジェクトを作成する関数です。

import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.runBlocking

fun generateNums(): Flow<Int> = flow {
    for (i in 1..5) {
        emit(i) 
    }
}

fun main() = runBlocking {
    val numbersFlow: Flow<Int> = generateNums()

    numbersFlow.collect { value ->
        print(value)
    }
}

collect関数

abstract suspend fun collect(collector: FlowCollector<T>)

既に何度も登場しましたがcollect関数はFlowからデータを収集するための関数です。収集されたデータはブロック内で参照することができます。またこの関数のようなFlowの収集を開始するオペレーターが呼び出されることでFlowは動き出します。

またsuspend関数として定義されているため通常の関数内からは呼び出せずコルーチン内で実装する必要があるためrunBlockingなどを使用する必要があります。

emit関数

emit関数はFlow内からデータを発行(emit)するための関数です。emit関数を使用してデータをストリームに送信します。

map関数

inline fun <T, R> Flow<T>.map(crossinline transform: suspend (value: T) -> R): Flow<R>

map関数は流れてきた値に対して任意の処理を加えて流し返す関数です。定義を見ればわかるように再度Flowオブジェクトを返します。

import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.flow.map

fun main() = runBlocking {
    val numFlow: Flow<Int> = flowOf(1, 2, 3, 4, 5)

    numFlow
      .map { value ->
              value * 2 
      }
      .collect { value ->
          print(value) // 2 4 6 8 10
      }
}

filter関数

inline fun <T> Flow<T>.filter(crossinline predicate: suspend (T) -> Boolean): Flow<T>

filter関数は流れてきた値に任意の条件でフィルタリングをかけて流し返す関数です。こちらも再度Flowオブジェクトを返します。

import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.flow.filter

fun main() = runBlocking {
    val numFlow: Flow<Int> = flowOf(1, 2, 3, 4, 5)

    numFlow
      .filter { value ->
            value % 2 == 0 
      }
      .collect { value ->
          print(value) // 2 4 
      }
}

flowOn関数

fun <T> Flow<T>.flowOn(context: CoroutineContext): Flow<T>

filter関数はFlow内でのコルーチンの実行スレッド(CoroutineContext)を変更するための関数です。こちらも再度Flowオブジェクトを返します。

import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.Dispatchers


fun main() = runBlocking {
    val numFlow: Flow<Int> = flowOf(1, 2, 3, 4, 5)

    numFlow
      .filter { value ->
            value % 2 == 0 
      }
      .map { value ->
              value * 2 
      }
      .flowOn(Dispatchers.IO)
      .collect { value ->
          print(value)
      }
}

例えば上記のような箇所にflowOnを入れ込んだ場合、flowOf〜filterまでは通常のデフォルトスレッド(メイン)で実行され、map〜collectに関してはDispatchers.IOというIOスレッド上で実行されます。

まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。

ご覧いただきありがとうございました。

searchbox

スポンサー

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑
今はCMSを使わずこのサイトを完全自作でサイト運営中〜

New Article

index